home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2000 March / maximum-cd-2000-03.iso / Quake3 Game Source / Q3AGameSource.exe / Main / g_bot.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-01-18  |  21.4 KB  |  964 lines

  1. // Copyright (C) 1999-2000 Id Software, Inc.
  2. //
  3. // g_bot.c
  4.  
  5. #include "g_local.h"
  6.  
  7.  
  8. static int        g_numBots;
  9. static char        *g_botInfos[MAX_BOTS];
  10.  
  11.  
  12. int                g_numArenas;
  13. static char        *g_arenaInfos[MAX_ARENAS];
  14.  
  15.  
  16. #define BOT_BEGIN_DELAY_BASE        2000
  17. #define BOT_BEGIN_DELAY_INCREMENT    1500
  18.  
  19. #define BOT_SPAWN_QUEUE_DEPTH    16
  20.  
  21. typedef struct {
  22.     int        clientNum;
  23.     int        spawnTime;
  24. } botSpawnQueue_t;
  25.  
  26. static int                botBeginDelay;
  27. static botSpawnQueue_t    botSpawnQueue[BOT_SPAWN_QUEUE_DEPTH];
  28.  
  29. vmCvar_t bot_minplayers;
  30.  
  31. extern gentity_t    *podium1;
  32. extern gentity_t    *podium2;
  33. extern gentity_t    *podium3;
  34.  
  35.  
  36. /*
  37. ===============
  38. G_ParseInfos
  39. ===============
  40. */
  41. int G_ParseInfos( char *buf, int max, char *infos[] ) {
  42.     char    *token;
  43.     int        count;
  44.     char    key[MAX_TOKEN_CHARS];
  45.     char    info[MAX_INFO_STRING];
  46.  
  47.     count = 0;
  48.  
  49.     while ( 1 ) {
  50.         token = COM_Parse( &buf );
  51.         if ( !token[0] ) {
  52.             break;
  53.         }
  54.         if ( strcmp( token, "{" ) ) {
  55.             Com_Printf( "Missing { in info file\n" );
  56.             break;
  57.         }
  58.  
  59.         if ( count == max ) {
  60.             Com_Printf( "Max infos exceeded\n" );
  61.             break;
  62.         }
  63.  
  64.         info[0] = '\0';
  65.         while ( 1 ) {
  66.             token = COM_ParseExt( &buf, qtrue );
  67.             if ( !token[0] ) {
  68.                 Com_Printf( "Unexpected end of info file\n" );
  69.                 break;
  70.             }
  71.             if ( !strcmp( token, "}" ) ) {
  72.                 break;
  73.             }
  74.             Q_strncpyz( key, token, sizeof( key ) );
  75.  
  76.             token = COM_ParseExt( &buf, qfalse );
  77.             if ( !token[0] ) {
  78.                 strcpy( token, "<NULL>" );
  79.             }
  80.             Info_SetValueForKey( info, key, token );
  81.         }
  82.         //NOTE: extra space for arena number
  83.         infos[count] = G_Alloc(strlen(info) + strlen("\\num\\") + strlen(va("%d", MAX_ARENAS)) + 1);
  84.         if (infos[count]) {
  85.             strcpy(infos[count], info);
  86.             count++;
  87.         }
  88.     }
  89.     return count;
  90. }
  91.  
  92. /*
  93. ===============
  94. G_LoadArenasFromFile
  95. ===============
  96. */
  97. static void G_LoadArenasFromFile( char *filename ) {
  98.     int                len;
  99.     fileHandle_t    f;
  100.     char            buf[MAX_ARENAS_TEXT];
  101.  
  102.     len = trap_FS_FOpenFile( filename, &f, FS_READ );
  103.     if ( !f ) {
  104.         trap_Printf( va( S_COLOR_RED "file not found: %s\n", filename ) );
  105.         return;
  106.     }
  107.     if ( len >= MAX_ARENAS_TEXT ) {
  108.         trap_Printf( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i", filename, len, MAX_ARENAS_TEXT ) );
  109.         trap_FS_FCloseFile( f );
  110.         return;
  111.     }
  112.  
  113.     trap_FS_Read( buf, len, f );
  114.     buf[len] = 0;
  115.     trap_FS_FCloseFile( f );
  116.  
  117.     g_numArenas += G_ParseInfos( buf, MAX_ARENAS - g_numArenas, &g_arenaInfos[g_numArenas] );
  118. }
  119.  
  120. /*
  121. ===============
  122. G_LoadArenas
  123. ===============
  124. */
  125. static void G_LoadArenas( void ) {
  126.     int            numdirs;
  127.     vmCvar_t    arenasFile;
  128.     char        filename[128];
  129.     char        dirlist[1024];
  130.     char*        dirptr;
  131.     int            i, n;
  132.     int            dirlen;
  133.  
  134.     g_numArenas = 0;
  135.  
  136.     trap_Cvar_Register( &arenasFile, "g_arenasFile", "", CVAR_INIT|CVAR_ROM );
  137.     if( *arenasFile.string ) {
  138.         G_LoadArenasFromFile(arenasFile.string);
  139.     }
  140.     else {
  141.         G_LoadArenasFromFile("scripts/arenas.txt");
  142.     }
  143.  
  144.     // get all arenas from .arena files
  145.     numdirs = trap_FS_GetFileList("scripts", ".arena", dirlist, 1024 );
  146.     dirptr  = dirlist;
  147.     for (i = 0; i < numdirs; i++, dirptr += dirlen+1) {
  148.         dirlen = strlen(dirptr);
  149.         strcpy(filename, "scripts/");
  150.         strcat(filename, dirptr);
  151.         G_LoadArenasFromFile(filename);
  152.     }
  153.     trap_Printf( va( "%i arenas parsed\n", g_numArenas ) );
  154.     
  155.     for( n = 0; n < g_numArenas; n++ ) {
  156.         Info_SetValueForKey( g_arenaInfos[n], "num", va( "%i", n ) );
  157.     }
  158. }
  159.  
  160.  
  161. /*
  162. ===============
  163. G_GetArenaInfoByNumber
  164. ===============
  165. */
  166. const char *G_GetArenaInfoByMap( const char *map ) {
  167.     int            n;
  168.  
  169.     for( n = 0; n < g_numArenas; n++ ) {
  170.         if( Q_stricmp( Info_ValueForKey( g_arenaInfos[n], "map" ), map ) == 0 ) {
  171.             return g_arenaInfos[n];
  172.         }
  173.     }
  174.  
  175.     return NULL;
  176. }
  177.  
  178.  
  179. /*
  180. =================
  181. PlayerIntroSound
  182. =================
  183. */
  184. static void PlayerIntroSound( const char *modelAndSkin ) {
  185.     char    model[MAX_QPATH];
  186.     char    *skin;
  187.  
  188.     Q_strncpyz( model, modelAndSkin, sizeof(model) );
  189.     skin = Q_strrchr( model, '/' );
  190.     if ( skin ) {
  191.         *skin++ = '\0';
  192.     }
  193.     else {
  194.         skin = model;
  195.     }
  196.  
  197.     if( Q_stricmp( skin, "default" ) == 0 ) {
  198.         skin = model;
  199.     }
  200.  
  201.     trap_SendConsoleCommand( EXEC_APPEND, va( "play sound/player/announce/%s.wav\n", skin ) );
  202. }
  203.  
  204. /*
  205. ===============
  206. G_AddRandomBot
  207. ===============
  208. */
  209. void G_AddRandomBot( int team ) {
  210.     int        i, n, num, skill;
  211.     char    *value, netname[36], *teamstr;
  212.     gclient_t    *cl;
  213.  
  214.     num = 0;
  215.     for ( n = 0; n < g_numBots ; n++ ) {
  216.         value = Info_ValueForKey( g_botInfos[n], "name" );
  217.         //
  218.         for ( i=0 ; i< g_maxclients.integer ; i++ ) {
  219.             cl = level.clients + i;
  220.             if ( cl->pers.connected != CON_CONNECTED ) {
  221.                 continue;
  222.             }
  223.             if ( !(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) ) {
  224.                 continue;
  225.             }
  226.             if ( team >= 0 && cl->sess.sessionTeam != team ) {
  227.                 continue;
  228.             }
  229.             if ( !Q_stricmp( value, cl->pers.netname ) ) {
  230.                 break;
  231.             }
  232.         }
  233.         if (i >= g_maxclients.integer) {
  234.             num++;
  235.         }
  236.     }
  237.     num = random() * num;
  238.     for ( n = 0; n < g_numBots ; n++ ) {
  239.         value = Info_ValueForKey( g_botInfos[n], "name" );
  240.         //
  241.         for ( i=0 ; i< g_maxclients.integer ; i++ ) {
  242.             cl = level.clients + i;
  243.             if ( cl->pers.connected != CON_CONNECTED ) {
  244.                 continue;
  245.             }
  246.             if ( !(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) ) {
  247.                 continue;
  248.             }
  249.             if ( team >= 0 && cl->sess.sessionTeam != team ) {
  250.                 continue;
  251.             }
  252.             if ( !Q_stricmp( value, cl->pers.netname ) ) {
  253.                 break;
  254.             }
  255.         }
  256.         if (i >= g_maxclients.integer) {
  257.             num--;
  258.             if (num <= 0) {
  259.                 skill = trap_Cvar_VariableIntegerValue( "g_spSkill" );
  260.                 if (team == TEAM_RED) teamstr = "red";
  261.                 else if (team == TEAM_BLUE) teamstr = "blue";
  262.                 else teamstr = "";
  263.                 strncpy(netname, value, sizeof(netname)-1);
  264.                 netname[sizeof(netname)-1] = '\0';
  265.                 Q_CleanStr(netname);
  266.                 trap_SendConsoleCommand( EXEC_INSERT, va("addbot %s %i %s %i\n", netname, skill, teamstr, 0) );
  267.                 return;
  268.             }
  269.         }
  270.     }
  271. }
  272.  
  273. /*
  274. ===============
  275. G_RemoveRandomBot
  276. ===============
  277. */
  278. int G_RemoveRandomBot( int team ) {
  279.     int i;
  280.     char netname[36];
  281.     gclient_t    *cl;
  282.  
  283.     for ( i=0 ; i< g_maxclients.integer ; i++ ) {
  284.         cl = level.clients + i;
  285.         if ( cl->pers.connected != CON_CONNECTED ) {
  286.             continue;
  287.         }
  288.         if ( !(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) ) {
  289.             continue;
  290.         }
  291.         if ( team >= 0 && cl->sess.sessionTeam != team ) {
  292.             continue;
  293.         }
  294.         strcpy(netname, cl->pers.netname);
  295.         Q_CleanStr(netname);
  296.         trap_SendConsoleCommand( EXEC_INSERT, va("kick %s\n", netname) );
  297.         return qtrue;
  298.     }
  299.     return qfalse;
  300. }
  301.  
  302. /*
  303. ===============
  304. G_CountHumanPlayers
  305. ===============
  306. */
  307. int G_CountHumanPlayers( int team ) {
  308.     int i, num;
  309.     gclient_t    *cl;
  310.  
  311.     num = 0;
  312.     for ( i=0 ; i< g_maxclients.integer ; i++ ) {
  313.         cl = level.clients + i;
  314.         if ( cl->pers.connected != CON_CONNECTED ) {
  315.             continue;
  316.         }
  317.         if ( g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT ) {
  318.             continue;
  319.         }
  320.         if ( team >= 0 && cl->sess.sessionTeam != team ) {
  321.             continue;
  322.         }
  323.         num++;
  324.     }
  325.     return num;
  326. }
  327.  
  328. /*
  329. ===============
  330. G_CountBotPlayers
  331. ===============
  332. */
  333. int G_CountBotPlayers( int team ) {
  334.     int i, n, num;
  335.     gclient_t    *cl;
  336.  
  337.     num = 0;
  338.     for ( i=0 ; i< g_maxclients.integer ; i++ ) {
  339.         cl = level.clients + i;
  340.         if ( cl->pers.connected != CON_CONNECTED ) {
  341.             continue;
  342.         }
  343.         if ( !(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) ) {
  344.             continue;
  345.         }
  346.         if ( team >= 0 && cl->sess.sessionTeam != team ) {
  347.             continue;
  348.         }
  349.         num++;
  350.     }
  351.     for( n = 0; n < BOT_SPAWN_QUEUE_DEPTH; n++ ) {
  352.         if( !botSpawnQueue[n].spawnTime ) {
  353.             continue;
  354.         }
  355.         if ( botSpawnQueue[n].spawnTime > level.time ) {
  356.             continue;
  357.         }
  358.         num++;
  359.     }
  360.     return num;
  361. }
  362.  
  363. /*
  364. ===============
  365. G_CheckMinimumPlayers
  366. ===============
  367. */
  368. void G_CheckMinimumPlayers( void ) {
  369.     int minplayers;
  370.     int humanplayers, botplayers;
  371.     static int checkminimumplayers_time;
  372.  
  373.     if (level.intermissiontime) return;
  374.     //only check once each 10 seconds
  375.     if (checkminimumplayers_time > level.time - 10000) {
  376.         return;
  377.     }
  378.     checkminimumplayers_time = level.time;
  379.     trap_Cvar_Update(&bot_minplayers);
  380.     minplayers = bot_minplayers.integer;
  381.     if (minplayers <= 0) return;
  382.  
  383.     if (g_gametype.integer >= GT_TEAM) {
  384.         if (minplayers >= g_maxclients.integer / 2) {
  385.             minplayers = (g_maxclients.integer / 2) -1;
  386.         }
  387.  
  388.         humanplayers = G_CountHumanPlayers( TEAM_RED );
  389.         botplayers = G_CountBotPlayers(    TEAM_RED );
  390.         //
  391.         if (humanplayers + botplayers < minplayers) {
  392.             G_AddRandomBot( TEAM_RED );
  393.         } else if (humanplayers + botplayers > minplayers && botplayers) {
  394.             G_RemoveRandomBot( TEAM_RED );
  395.         }
  396.         //
  397.         humanplayers = G_CountHumanPlayers( TEAM_BLUE );
  398.         botplayers = G_CountBotPlayers( TEAM_BLUE );
  399.         //
  400.         if (humanplayers + botplayers < minplayers) {
  401.             G_AddRandomBot( TEAM_BLUE );
  402.         } else if (humanplayers + botplayers > minplayers && botplayers) {
  403.             G_RemoveRandomBot( TEAM_BLUE );
  404.         }
  405.     }
  406.     else if (g_gametype.integer == GT_TOURNAMENT) {
  407.         if (minplayers >= g_maxclients.integer) {
  408.             minplayers = g_maxclients.integer-1;
  409.         }
  410.         humanplayers = G_CountHumanPlayers( -1 );
  411.         botplayers = G_CountBotPlayers( -1 );
  412.         //
  413.         if (humanplayers + botplayers < minplayers) {
  414.             G_AddRandomBot( TEAM_FREE );
  415.         } else if (humanplayers + botplayers > minplayers && botplayers) {
  416.             // try to remove spectators first
  417.             if (!G_RemoveRandomBot( TEAM_SPECTATOR )) {
  418.                 // just remove the bot that is playing
  419.                 G_RemoveRandomBot( -1 );
  420.             }
  421.         }
  422.     }
  423.     else if (g_gametype.integer == GT_FFA) {
  424.         if (minplayers >= g_maxclients.integer) {
  425.             minplayers = g_maxclients.integer-1;
  426.         }
  427.         humanplayers = G_CountHumanPlayers( TEAM_FREE );
  428.         botplayers = G_CountBotPlayers( TEAM_FREE );
  429.         //
  430.         if (humanplayers + botplayers < minplayers) {
  431.             G_AddRandomBot( TEAM_FREE );
  432.         } else if (humanplayers + botplayers > minplayers && botplayers) {
  433.             G_RemoveRandomBot( TEAM_FREE );
  434.         }
  435.     }
  436. }
  437.  
  438. /*
  439. ===============
  440. G_CheckBotSpawn
  441. ===============
  442. */
  443. void G_CheckBotSpawn( void ) {
  444.     int        n;
  445.     char    userinfo[MAX_INFO_VALUE];
  446.  
  447.     G_CheckMinimumPlayers();
  448.  
  449.     for( n = 0; n < BOT_SPAWN_QUEUE_DEPTH; n++ ) {
  450.         if( !botSpawnQueue[n].spawnTime ) {
  451.             continue;
  452.         }
  453.         if ( botSpawnQueue[n].spawnTime > level.time ) {
  454.             continue;
  455.         }
  456.         ClientBegin( botSpawnQueue[n].clientNum );
  457.         botSpawnQueue[n].spawnTime = 0;
  458.  
  459.         if( g_gametype.integer == GT_SINGLE_PLAYER ) {
  460.             trap_GetUserinfo( botSpawnQueue[n].clientNum, userinfo, sizeof(userinfo) );
  461.             PlayerIntroSound( Info_ValueForKey (userinfo, "model") );
  462.         }
  463.     }
  464. }
  465.  
  466.  
  467. /*
  468. ===============
  469. AddBotToSpawnQueue
  470. ===============
  471. */
  472. static void AddBotToSpawnQueue( int clientNum, int delay ) {
  473.     int        n;
  474.  
  475.     for( n = 0; n < BOT_SPAWN_QUEUE_DEPTH; n++ ) {
  476.         if( !botSpawnQueue[n].spawnTime ) {
  477.             botSpawnQueue[n].spawnTime = level.time + delay;
  478.             botSpawnQueue[n].clientNum = clientNum;
  479.             return;
  480.         }
  481.     }
  482.  
  483.     G_Printf( S_COLOR_YELLOW "Unable to delay spawn\n" );
  484.     ClientBegin( clientNum );
  485. }
  486.  
  487.  
  488. /*
  489. ===============
  490. G_QueueBotBegin
  491. ===============
  492. */
  493. void G_QueueBotBegin( int clientNum ) {
  494.     AddBotToSpawnQueue( clientNum, botBeginDelay );
  495.     botBeginDelay += BOT_BEGIN_DELAY_INCREMENT;
  496. }
  497.  
  498.  
  499. /*
  500. ===============
  501. G_BotConnect
  502. ===============
  503. */
  504. qboolean G_BotConnect( int clientNum, qboolean restart ) {
  505.     bot_settings_t    settings;
  506.     char            userinfo[MAX_INFO_STRING];
  507.  
  508.     trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) );
  509.  
  510.     Q_strncpyz( settings.characterfile, Info_ValueForKey( userinfo, "characterfile" ), sizeof(settings.characterfile) );
  511.     settings.skill = atoi( Info_ValueForKey( userinfo, "skill" ) );
  512.     Q_strncpyz( settings.team, Info_ValueForKey( userinfo, "team" ), sizeof(settings.team) );
  513.  
  514.     if (!BotAISetupClient( clientNum, &settings )) {
  515.         trap_DropClient( clientNum, "BotAISetupClient failed" );
  516.         return qfalse;
  517.     }
  518.  
  519.     if( restart && g_gametype.integer == GT_SINGLE_PLAYER ) {
  520.         g_entities[clientNum].botDelayBegin = qtrue;
  521.     }
  522.     else {
  523.         g_entities[clientNum].botDelayBegin = qfalse;
  524.     }
  525.  
  526.     return qtrue;
  527. }
  528.  
  529.  
  530. /*
  531. ===============
  532. G_AddBot
  533. ===============
  534. */
  535. static void G_AddBot( const char *name, int skill, const char *team, int delay, char *altname) {
  536.     int                clientNum;
  537.     char            *botinfo;
  538.     gentity_t        *bot;
  539.     char            *key;
  540.     char            *s;
  541.     char            *botname;
  542.     char            *model;
  543.     char            userinfo[MAX_INFO_STRING];
  544.  
  545.     // get the botinfo from bots.txt
  546.     botinfo = G_GetBotInfoByName( name );
  547.     if ( !botinfo ) {
  548.         G_Printf( S_COLOR_RED "Error: Bot '%s' not defined\n", name );
  549.         return;
  550.     }
  551.  
  552.     // create the bot's userinfo
  553.     userinfo[0] = '\0';
  554.  
  555.     botname = Info_ValueForKey( botinfo, "funname" );
  556.     if( !botname[0] ) {
  557.         botname = Info_ValueForKey( botinfo, "name" );
  558.     }
  559.     // check for an alternative name
  560.     if (altname && altname[0]) {
  561.         botname = altname;
  562.     }
  563.     Info_SetValueForKey( userinfo, "name", botname );
  564.     Info_SetValueForKey( userinfo, "rate", "25000" );
  565.     Info_SetValueForKey( userinfo, "snaps", "20" );
  566.     Info_SetValueForKey( userinfo, "skill", va("%i", skill) );
  567.  
  568.     if ( skill == 1 ) {
  569.         Info_SetValueForKey( userinfo, "handicap", "50" );
  570.     }
  571.     else if ( skill == 2 ) {
  572.         Info_SetValueForKey( userinfo, "handicap", "70" );
  573.     }
  574.     else if ( skill == 3 ) {
  575.         Info_SetValueForKey( userinfo, "handicap", "90" );
  576.     }
  577.  
  578.     key = "model";
  579.     model = Info_ValueForKey( botinfo, key );
  580.     if ( !*model ) {
  581.         model = "visor/default";
  582.     }
  583.     Info_SetValueForKey( userinfo, key, model );
  584.  
  585.     key = "gender";
  586.     s = Info_ValueForKey( botinfo, key );
  587.     if ( !*s ) {
  588.         s = "male";
  589.     }
  590.     Info_SetValueForKey( userinfo, "sex", s );
  591.  
  592.     key = "color";
  593.     s = Info_ValueForKey( botinfo, key );
  594.     if ( !*s ) {
  595.         s = "4";
  596.     }
  597.     Info_SetValueForKey( userinfo, key, s );
  598.  
  599.     s = Info_ValueForKey(botinfo, "aifile");
  600.     if (!*s ) {
  601.         trap_Printf( S_COLOR_RED "Error: bot has no aifile specified\n" );
  602.         return;
  603.     }
  604.  
  605.     // have the server allocate a client slot
  606.     clientNum = trap_BotAllocateClient();
  607.     if ( clientNum == -1 ) {
  608.         G_Printf( S_COLOR_RED "Unable to add bot.  All player slots are in use.\n" );
  609.         G_Printf( S_COLOR_RED "Start server with more 'open' slots (or check setting of sv_maxclients cvar).\n" );
  610.         return;
  611.     }
  612.  
  613.     // initialize the bot settings
  614.     if( !team || !*team ) {
  615.         if( g_gametype.integer == GT_TEAM || g_gametype.integer == GT_CTF) {
  616.             if( PickTeam(clientNum) == TEAM_RED) {
  617.                 team = "red";
  618.             }
  619.             else {
  620.                 team = "blue";
  621.             }
  622.         }
  623.         else {
  624.             team = "red";
  625.         }
  626.     }
  627.     Info_SetValueForKey( userinfo, "characterfile", Info_ValueForKey( botinfo, "aifile" ) );
  628.     Info_SetValueForKey( userinfo, "skill", va( "%i", skill ) );
  629.     Info_SetValueForKey( userinfo, "team", team );
  630.  
  631.     bot = &g_entities[ clientNum ];
  632.     bot->r.svFlags |= SVF_BOT;
  633.     bot->inuse = qtrue;
  634.  
  635.     // register the userinfo
  636.     trap_SetUserinfo( clientNum, userinfo );
  637.  
  638.     // have it connect to the game as a normal client
  639.     if ( ClientConnect( clientNum, qtrue, qtrue ) ) {
  640.         return;
  641.     }
  642.  
  643.     if( delay == 0 ) {
  644.         ClientBegin( clientNum );
  645.         return;
  646.     }
  647.  
  648.     AddBotToSpawnQueue( clientNum, delay );
  649. }
  650.  
  651.  
  652. /*
  653. ===============
  654. Svcmd_AddBot_f
  655. ===============
  656. */
  657. void Svcmd_AddBot_f( void ) {
  658.     int                skill;
  659.     int                delay;
  660.     char            name[MAX_TOKEN_CHARS];
  661.     char            altname[MAX_TOKEN_CHARS];
  662.     char            string[MAX_TOKEN_CHARS];
  663.     char            team[MAX_TOKEN_CHARS];
  664.  
  665.     // are bots enabled?
  666.     if ( !trap_Cvar_VariableIntegerValue( "bot_enable" ) ) {
  667.         return;
  668.     }
  669.  
  670.     // name
  671.     trap_Argv( 1, name, sizeof( name ) );
  672.     if ( !name[0] ) {
  673.         trap_Printf( "Usage: Addbot <botname> [skill 1-4] [team] [msec delay] [altname]\n" );
  674.         return;
  675.     }
  676.  
  677.     // skill
  678.     trap_Argv( 2, string, sizeof( string ) );
  679.     if ( !string[0] ) {
  680.         skill = 4;
  681.     }
  682.     else {
  683.         skill = atoi( string );
  684.     }
  685.  
  686.     // team
  687.     trap_Argv( 3, team, sizeof( team ) );
  688.  
  689.     // delay
  690.     trap_Argv( 4, string, sizeof( string ) );
  691.     if ( !string[0] ) {
  692.         delay = 0;
  693.     }
  694.     else {
  695.         delay = atoi( string );
  696.     }
  697.  
  698.     // alternative name
  699.     trap_Argv( 5, altname, sizeof( altname ) );
  700.  
  701.     G_AddBot( name, skill, team, delay, altname );
  702.  
  703.     // if this was issued during gameplay and we are playing locally,
  704.     // go ahead and load the bot's media immediately
  705.     if ( level.time - level.startTime > 1000 &&
  706.         trap_Cvar_VariableIntegerValue( "cl_running" ) ) {
  707.         trap_SendServerCommand( -1, "loaddefered\n" );    // FIXME: spelled wrong, but not changing for demo
  708.     }
  709. }
  710.  
  711. /*
  712. ===============
  713. Svcmd_BotList_f
  714. ===============
  715. */
  716. void Svcmd_BotList_f( void ) {
  717.     int i;
  718.     char name[MAX_TOKEN_CHARS];
  719.     char funname[MAX_TOKEN_CHARS];
  720.     char model[MAX_TOKEN_CHARS];
  721.     char aifile[MAX_TOKEN_CHARS];
  722.  
  723.     trap_Printf("^1name             model            aifile              funname\n");
  724.     for (i = 0; i < g_numBots; i++) {
  725.         strcpy(name, Info_ValueForKey( g_botInfos[i], "name" ));
  726.         if ( !*name ) {
  727.             strcpy(name, "UnnamedPlayer");
  728.         }
  729.         strcpy(funname, Info_ValueForKey( g_botInfos[i], "funname" ));
  730.         if ( !*funname ) {
  731.             strcpy(funname, "");
  732.         }
  733.         strcpy(model, Info_ValueForKey( g_botInfos[i], "model" ));
  734.         if ( !*model ) {
  735.             strcpy(model, "visor/default");
  736.         }
  737.         strcpy(aifile, Info_ValueForKey( g_botInfos[i], "aifile"));
  738.         if (!*aifile ) {
  739.             strcpy(aifile, "bots/default_c.c");
  740.         }
  741.         trap_Printf(va("%-16s %-16s %-20s %-20s\n", name, model, aifile, funname));
  742.     }
  743. }
  744.  
  745.  
  746. /*
  747. ===============
  748. G_SpawnBots
  749. ===============
  750. */
  751. static void G_SpawnBots( char *botList, int baseDelay ) {
  752.     char        *bot;
  753.     char        *p;
  754.     int            skill;
  755.     int            delay;
  756.     char        bots[MAX_INFO_VALUE];
  757.  
  758.     podium1 = NULL;
  759.     podium2 = NULL;
  760.     podium3 = NULL;
  761.  
  762.     skill = trap_Cvar_VariableIntegerValue( "g_spSkill" );
  763.     if( skill < 1 || skill > 5 ) {
  764.         trap_Cvar_Set( "g_spSkill", "2" );
  765.         skill = 2;
  766.     }
  767.  
  768.     Q_strncpyz( bots, botList, sizeof(bots) );
  769.     p = &bots[0];
  770.     delay = baseDelay;
  771.     while( *p ) {
  772.         //skip spaces
  773.         while( *p && *p == ' ' ) {
  774.             p++;
  775.         }
  776.         if( !p ) {
  777.             break;
  778.         }
  779.  
  780.         // mark start of bot name
  781.         bot = p;
  782.  
  783.         // skip until space of null
  784.         while( *p && *p != ' ' ) {
  785.             p++;
  786.         }
  787.         if( *p ) {
  788.             *p++ = 0;
  789.         }
  790.  
  791.         // we must add the bot this way, calling G_AddBot directly at this stage
  792.         // does "Bad Things"
  793.         trap_SendConsoleCommand( EXEC_INSERT, va("addbot %s %i free %i\n", bot, skill, delay) );
  794.  
  795.         delay += BOT_BEGIN_DELAY_INCREMENT;
  796.     }
  797. }
  798.  
  799.  
  800. /*
  801. ===============
  802. G_LoadBotsFromFile
  803. ===============
  804. */
  805. static void G_LoadBotsFromFile( char *filename ) {
  806.     int                len;
  807.     fileHandle_t    f;
  808.     char            buf[MAX_BOTS_TEXT];
  809.  
  810.     len = trap_FS_FOpenFile( filename, &f, FS_READ );
  811.     if ( !f ) {
  812.         trap_Printf( va( S_COLOR_RED "file not found: %s\n", filename ) );
  813.         return;
  814.     }
  815.     if ( len >= MAX_BOTS_TEXT ) {
  816.         trap_Printf( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i", filename, len, MAX_BOTS_TEXT ) );
  817.         trap_FS_FCloseFile( f );
  818.         return;
  819.     }
  820.  
  821.     trap_FS_Read( buf, len, f );
  822.     buf[len] = 0;
  823.     trap_FS_FCloseFile( f );
  824.  
  825.     g_numBots += G_ParseInfos( buf, MAX_BOTS - g_numBots, &g_botInfos[g_numBots] );
  826. }
  827.  
  828. /*
  829. ===============
  830. G_LoadBots
  831. ===============
  832. */
  833. static void G_LoadBots( void ) {
  834.     vmCvar_t    botsFile;
  835.     int            numdirs;
  836.     char        filename[128];
  837.     char        dirlist[1024];
  838.     char*        dirptr;
  839.     int            i;
  840.     int            dirlen;
  841.  
  842.     if ( !trap_Cvar_VariableIntegerValue( "bot_enable" ) ) {
  843.         return;
  844.     }
  845.  
  846.     g_numBots = 0;
  847.  
  848.     trap_Cvar_Register( &botsFile, "g_botsFile", "", CVAR_INIT|CVAR_ROM );
  849.     if( *botsFile.string ) {
  850.         G_LoadBotsFromFile(botsFile.string);
  851.     }
  852.     else {
  853.         G_LoadBotsFromFile("scripts/bots.txt");
  854.     }
  855.  
  856.     // get all bots from .bot files
  857.     numdirs = trap_FS_GetFileList("scripts", ".bot", dirlist, 1024 );
  858.     dirptr  = dirlist;
  859.     for (i = 0; i < numdirs; i++, dirptr += dirlen+1) {
  860.         dirlen = strlen(dirptr);
  861.         strcpy(filename, "scripts/");
  862.         strcat(filename, dirptr);
  863.         G_LoadBotsFromFile(filename);
  864.     }
  865.     trap_Printf( va( "%i bots parsed\n", g_numBots ) );
  866. }
  867.  
  868.  
  869.  
  870. /*
  871. ===============
  872. G_GetBotInfoByNumber
  873. ===============
  874. */
  875. char *G_GetBotInfoByNumber( int num ) {
  876.     if( num < 0 || num >= g_numBots ) {
  877.         trap_Printf( va( S_COLOR_RED "Invalid bot number: %i\n", num ) );
  878.         return NULL;
  879.     }
  880.     return g_botInfos[num];
  881. }
  882.  
  883.  
  884. /*
  885. ===============
  886. G_GetBotInfoByName
  887. ===============
  888. */
  889. char *G_GetBotInfoByName( const char *name ) {
  890.     int        n;
  891.     char    *value;
  892.  
  893.     for ( n = 0; n < g_numBots ; n++ ) {
  894.         value = Info_ValueForKey( g_botInfos[n], "name" );
  895.         if ( !Q_stricmp( value, name ) ) {
  896.             return g_botInfos[n];
  897.         }
  898.     }
  899.  
  900.     return NULL;
  901. }
  902.  
  903. /*
  904. ===============
  905. G_InitBots
  906. ===============
  907. */
  908. void G_InitBots( qboolean restart ) {
  909.     int            fragLimit;
  910.     int            timeLimit;
  911.     const char    *arenainfo;
  912.     char        *strValue;
  913.     int            basedelay;
  914.     char        map[MAX_QPATH];
  915.     char        serverinfo[MAX_INFO_STRING];
  916.  
  917.     G_LoadBots();
  918.     G_LoadArenas();
  919.  
  920.     trap_Cvar_Register( &bot_minplayers, "bot_minplayers", "0", CVAR_SERVERINFO );
  921.  
  922.     if( g_gametype.integer == GT_SINGLE_PLAYER ) {
  923.         trap_GetServerinfo( serverinfo, sizeof(serverinfo) );
  924.         Q_strncpyz( map, Info_ValueForKey( serverinfo, "mapname" ), sizeof(map) );
  925.         arenainfo = G_GetArenaInfoByMap( map );
  926.         if ( !arenainfo ) {
  927.             return;
  928.         }
  929.  
  930.         strValue = Info_ValueForKey( arenainfo, "fraglimit" );
  931.         fragLimit = atoi( strValue );
  932.         if ( fragLimit ) {
  933.             trap_Cvar_Set( "fraglimit", strValue );
  934.         }
  935.         else {
  936.             trap_Cvar_Set( "fraglimit", "0" );
  937.         }
  938.  
  939.         strValue = Info_ValueForKey( arenainfo, "timelimit" );
  940.         timeLimit = atoi( strValue );
  941.         if ( timeLimit ) {
  942.             trap_Cvar_Set( "timelimit", strValue );
  943.         }
  944.         else {
  945.             trap_Cvar_Set( "timelimit", "0" );
  946.         }
  947.  
  948.         if ( !fragLimit && !timeLimit ) {
  949.             trap_Cvar_Set( "fraglimit", "10" );
  950.             trap_Cvar_Set( "timelimit", "0" );
  951.         }
  952.  
  953.         basedelay = BOT_BEGIN_DELAY_BASE;
  954.         strValue = Info_ValueForKey( arenainfo, "special" );
  955.         if( Q_stricmp( strValue, "training" ) == 0 ) {
  956.             basedelay += 10000;
  957.         }
  958.  
  959.         if( !restart ) {
  960.             G_SpawnBots( Info_ValueForKey( arenainfo, "bots" ), basedelay );
  961.         }
  962.     }
  963. }
  964.